/* main.c - Application main entry point */

/*
 * Copyright (c) 2022 Xiaomi Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <sys/printk.h>
#include <sys/byteorder.h>
#include <zephyr.h>

#include <settings/settings.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
#include <bluetooth/mesh.h>

#include <logging/log.h>
LOG_MODULE_REGISTER(mesh);

#include "mible_api.h"
#include "mible_mesh_api.h"
#include "mible_mesh_device.h"
#include "custom_mi_config.h"

#include <../../subsys/bluetooth/mesh/net.h>
#include <../../subsys/bluetooth/mesh/access.h>
#include <../../subsys/bluetooth/mesh/transport.h>
#include <../../subsys/bluetooth/mesh/foundation.h>

#include <sys/boardctl.h>

static void mesh_process_handler(void * arg)
{
	while (mible_mesh_event_pending()) {
		mible_mesh_device_main_thread();
	}
}

static void event_post(mible_mesh_event_type_t type, void * data)
{
	mible_mesh_event_callback(type, data);

	MIBLE_TASK_POST_SINGLE(mesh_process_handler, NULL);
}

void mible_iv_update(uint32_t iv_index, bool iv_update)
{
	mible_mesh_iv_t iv = {
		.iv_index = iv_index,
		.flags = iv_update,
	};

	mible_mesh_event_callback(MIBLE_MESH_EVENT_IV_UPDATE, &iv);
}

static void process_mesh_node_init_event(void)
{
	mible_mesh_template_map_t node_map[5] = {
		[0] = {
            		.siid = 0,
            		.piid = 0,
            		.model_id = MIBLE_MESH_MIOT_SPEC_SERVER_MODEL,
            		.company_id = MIBLE_MESH_COMPANY_ID_XIAOMI,
            		.element = 0,
            		.appkey_idx = 0,
        	},
		[1] = {
        		.siid = 0,
			.piid = 0,
			.model_id = MIBLE_MESH_MIJIA_SERVER_MODEL,
			.company_id = MIBLE_MESH_COMPANY_ID_XIAOMI,
			.element = 0,
			.appkey_idx = 0,
		},
#if defined(MI_MESH_TEMPLATE_LIGHTNESS)		|| \
    defined(MI_MESH_TEMPLATE_LIGHTCTL)		|| \
    defined(MI_MESH_TEMPLATE_ONE_KEY_SWITCH)	|| \
    defined(MI_MESH_TEMPLATE_TWO_KEY_SWITCH)	|| \
    defined(MI_MESH_TEMPLATE_THREE_KEY_SWITCH)	|| \
    defined(MI_MESH_TEMPLATE_FAN)
		[2] = {
            		.siid = 2,
            		.piid = 1,
            		.model_id = MIBLE_MESH_MODEL_ID_GENERIC_ONOFF_SERVER,
            		.company_id = MIBLE_MESH_COMPANY_ID_SIG,
            		.element = 0,
            		.appkey_idx = 0,
        	},
#if defined(MI_MESH_TEMPLATE_TWO_KEY_SWITCH)	|| \
    defined(MI_MESH_TEMPLATE_THREE_KEY_SWITCH)
		[3] = {
            		.siid = 3,
            		.piid = 1,
            		.model_id = MIBLE_MESH_MODEL_ID_GENERIC_ONOFF_SERVER,
            		.company_id = MIBLE_MESH_COMPANY_ID_SIG,
            		.element = 1,
            		.appkey_idx = 0,
        	},
#if defined(MI_MESH_TEMPLATE_THREE_KEY_SWITCH)
		[4] = {
            		.siid = 4,
            		.piid = 1,
            		.model_id = MIBLE_MESH_MODEL_ID_GENERIC_ONOFF_SERVER,
            		.company_id = MIBLE_MESH_COMPANY_ID_SIG,
            		.element = 2,
            		.appkey_idx = 0,
        	},
#endif
#elif defined(MI_MESH_TEMPLATE_LIGHTNESS)	|| \
      defined(MI_MESH_TEMPLATE_LIGHTCTL)
		[3] = {
            		.siid = 2,
            		.piid = 2,
            		.model_id = MIBLE_MESH_MODEL_ID_LIGHTNESS_SERVER,
            		.company_id = MIBLE_MESH_COMPANY_ID_SIG,
            		.element = 0,
            		.appkey_idx = 0,
        	},
#if defined(MI_MESH_TEMPLATE_LIGHTCTL)
		[4] = {
            		.siid = 2,
            		.piid = 3,
            		.model_id = MIBLE_MESH_MODEL_ID_CTL_TEMPEATURE_SERVER,
            		.company_id = MIBLE_MESH_COMPANY_ID_SIG,
            		.element = 1,
            		.appkey_idx = 0,
        	},
#endif
#endif
#endif
	};

	mible_mesh_node_init_t node_info = {
		.map = node_map,
	};

	node_info.provisioned = bt_mesh_is_provisioned();

	if (node_info.provisioned) {
		node_info.address = bt_mesh_comp_get()->elem[0].addr;
		node_info.ivi = bt_mesh.iv_index;
	}

	event_post(MIBLE_MESH_EVENT_DEVICE_INIT_DONE, &node_info);
}

int mible_mesh_device_init_node(void)
{
	process_mesh_node_init_event();

	return MI_SUCCESS;
}

int mible_mesh_config_save(void);

int mible_mesh_device_provsion_done(void)
{
	mible_mesh_config_save();

	process_mesh_node_init_event();

	return MI_SUCCESS;
}

static int sig_msg_recv(struct bt_mesh_model *model,
			 struct bt_mesh_msg_ctx *ctx,
			 struct net_buf_simple *buf)
{
	mible_mesh_event_params_t evt = {
		.generic_msg = {
			.opcode = {
				.company_id = MIBLE_MESH_COMPANY_ID_SIG,
				.opcode = BT_MESH_MODEL_OP_2(buf->data[-2], buf->data[-1]),
			},
			.buf_len = buf->len,
			.meta_data = {
				.rssi = ctx->recv_rssi,
				.src_addr = ctx->addr,
				.dst_addr = ctx->recv_dst,
				.appkey_index = ctx->app_idx,
				.netkey_index = ctx->net_idx,
				.elem_index = model->elem_idx,
			},
		},
	};

	if (buf->len > 8) {
		evt.generic_msg.p_buf = buf->data;
	} else {
		(void)memcpy(evt.generic_msg.buff, buf->data, buf->len);
	}

	event_post(MIBLE_MESH_EVENT_GENERIC_MESSAGE_CB, &evt);

	return 0;
}

static int msg_ignore(struct bt_mesh_model *model,
		      struct bt_mesh_msg_ctx *ctx,
		      struct net_buf_simple *buf)
{
	return 0;
}

static const struct bt_mesh_model_op gen_onoff_srv_op[] = {
	{ MIBLE_MESH_MSG_GENERIC_ONOFF_GET, 0, sig_msg_recv },
	{ MIBLE_MESH_MSG_GENERIC_ONOFF_SET, 0, sig_msg_recv },
	{ MIBLE_MESH_MSG_GENERIC_ONOFF_SET_UNACKNOWLEDGED, 0, sig_msg_recv },
	{ MIBLE_MESH_MSG_GENERIC_ONOFF_STATUS, 0, msg_ignore },
	BT_MESH_MODEL_OP_END,
};

static const struct bt_mesh_model_op light_lightness_srv_op[] = {
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_GET,		0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_SET,		0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_SET_UNACKNOWLEDGED,		0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_LINEAR_GET,	0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_LINEAR_SET,	0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_LINEAR_SET_UNACKNOWLEDGED,	0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_LAST_GET,	0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_DEFAULT_GET,	0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_DEFAULT_SET_UNACKNOWLEDGED,	0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_RANGE_GET,	0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_RANGE_SET_UNACKNOWLEDGED,	0, sig_msg_recv },

	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_STATUS,		0, msg_ignore },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_LINEAR_STATUS,		0, msg_ignore },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_LAST_STATUS,		0, msg_ignore },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_DEFAULT_STATUS,	0, msg_ignore },
	{ MIBLE_MESH_MSG_LIGHT_LIGHTNESS_RANGE_STATUS,		0, msg_ignore },

	BT_MESH_MODEL_OP_END,
};

static const struct bt_mesh_model_op light_ctl_tmp_srv_op[] = {
	{ MIBLE_MESH_MSG_LIGHT_CTL_TEMPERATURE_GET,		0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_CTL_TEMPERATURE_SET,		0, sig_msg_recv },
	{ MIBLE_MESH_MSG_LIGHT_CTL_TEMPERATURE_SET_UNACKNOWLEDGED,		0, sig_msg_recv },

	{ MIBLE_MESH_MSG_LIGHT_CTL_TEMPERATURE_STATUS,		0, msg_ignore },

	BT_MESH_MODEL_OP_END,
};

static int default_ttl_get(struct bt_mesh_model *model,
			    struct bt_mesh_msg_ctx *ctx,
			    struct net_buf_simple *buf)
{
	BT_MESH_MODEL_BUF_DEFINE(msg, OP_DEFAULT_TTL_STATUS, 1);

	LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u",
		ctx->net_idx, ctx->app_idx, ctx->addr, buf->len);

	bt_mesh_model_msg_init(&msg, OP_DEFAULT_TTL_STATUS);
	net_buf_simple_add_u8(&msg, bt_mesh_default_ttl_get());

	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
		LOG_ERR("Unable to send Default TTL Status response");
	}

	return 0;
}

static int default_ttl_set(struct bt_mesh_model *model,
			    struct bt_mesh_msg_ctx *ctx,
			    struct net_buf_simple *buf)
{
	BT_MESH_MODEL_BUF_DEFINE(msg, OP_DEFAULT_TTL_STATUS, 1);
	int err;

	LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u",
		ctx->net_idx, ctx->app_idx, ctx->addr, buf->len);

	err = bt_mesh_default_ttl_set(buf->data[0]);
	if (err) {
		LOG_ERR("Prohibited Default TTL value 0x%02x", buf->data[0]);
		return err;
	}

	mible_mesh_event_params_t evt = {
		.config_msg = {
			.opcode = {
				.company_id = MIBLE_MESH_COMPANY_ID_SIG,
				.opcode = MIBLE_MESH_MSG_CONFIG_DEFAULT_TTL_SET,
			},
			.meta_data = {
				.rssi = ctx->recv_rssi,
				.src_addr = ctx->addr,
				.dst_addr = ctx->recv_dst,
				.appkey_index = ctx->app_idx,
				.netkey_index = ctx->net_idx,
				.elem_index = model->elem_idx,
			},
			{
				.ttl_set = {
					.ttl = buf->data[0]
				},
			},
		},
	};

	event_post(MIBLE_MESH_EVENT_CONFIG_MESSAGE_CB, &evt);

	bt_mesh_model_msg_init(&msg, OP_DEFAULT_TTL_STATUS);
	net_buf_simple_add_u8(&msg, buf->data[0]);

	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
		LOG_ERR("Unable to send Default TTL Status response");
	}

	return 0;
}

static int net_transmit_get(struct bt_mesh_model *model,
			     struct bt_mesh_msg_ctx *ctx,
			     struct net_buf_simple *buf)
{
	BT_MESH_MODEL_BUF_DEFINE(msg, OP_NET_TRANSMIT_STATUS, 1);

	LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u",
		ctx->net_idx, ctx->app_idx, ctx->addr, buf->len);

	bt_mesh_model_msg_init(&msg, OP_NET_TRANSMIT_STATUS);
	net_buf_simple_add_u8(&msg, bt_mesh_net_transmit_get());

	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
		LOG_ERR("Unable to send Config Network Transmit Status");
	}

	return 0;
}

static int net_transmit_set(struct bt_mesh_model *model,
			     struct bt_mesh_msg_ctx *ctx,
			     struct net_buf_simple *buf)
{
	BT_MESH_MODEL_BUF_DEFINE(msg, OP_NET_TRANSMIT_STATUS, 1);

	LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u",
		ctx->net_idx, ctx->app_idx, ctx->addr, buf->len);

	LOG_DBG("Transmit 0x%02x (count %u interval %ums)", buf->data[0],
	        BT_MESH_TRANSMIT_COUNT(buf->data[0]),
	        BT_MESH_TRANSMIT_INT(buf->data[0]));

	bt_mesh_net_transmit_set(buf->data[0]);

	mible_mesh_event_params_t evt = {
		.config_msg = {
			.opcode = {
				.company_id = MIBLE_MESH_COMPANY_ID_SIG,
				.opcode = MIBLE_MESH_MSG_CONFIG_NETWORK_TRANSMIT_SET,
			},
			.meta_data = {
				.rssi = ctx->recv_rssi,
				.src_addr = ctx->addr,
				.dst_addr = ctx->recv_dst,
				.appkey_index = ctx->app_idx,
				.netkey_index = ctx->net_idx,
				.elem_index = model->elem_idx,
			},
			{
				.nettx_set = {
					.net_retrans_cnt = BT_MESH_TRANSMIT_COUNT(buf->data[0]),
					.net_retrans_intvlsteps = ((buf->data[0])  >> 3),
				}
			},
		},
	};

	event_post(MIBLE_MESH_EVENT_CONFIG_MESSAGE_CB, &evt);

	bt_mesh_model_msg_init(&msg, OP_NET_TRANSMIT_STATUS);
	net_buf_simple_add_u8(&msg, buf->data[0]);

	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
		LOG_ERR("Unable to send Network Transmit Status");
	}

	return 0;
}

static int relay_get(struct bt_mesh_model *model,
		      struct bt_mesh_msg_ctx *ctx,
		      struct net_buf_simple *buf)
{
	BT_MESH_MODEL_BUF_DEFINE(msg, OP_RELAY_STATUS, 2);

	LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u",
		ctx->net_idx, ctx->app_idx, ctx->addr, buf->len);

	bt_mesh_model_msg_init(&msg, OP_RELAY_STATUS);
	net_buf_simple_add_u8(&msg, bt_mesh_relay_get());
	net_buf_simple_add_u8(&msg, bt_mesh_relay_retransmit_get());

	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
		LOG_ERR("Unable to send Config Relay Status response");
	}

	return 0;
}

static int relay_set(struct bt_mesh_model *model,
		      struct bt_mesh_msg_ctx *ctx,
		      struct net_buf_simple *buf)
{
	BT_MESH_MODEL_BUF_DEFINE(msg, OP_RELAY_STATUS, 2);

	LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u",
		ctx->net_idx, ctx->app_idx, ctx->addr, buf->len);

	if (buf->data[0] != 0x00 && buf->data[0] != 0x01) {
		LOG_WRN("Invalid Relay value 0x%02x", buf->data[0]);
		return -EINVAL;
	}

	(void)bt_mesh_relay_set(buf->data[0], buf->data[1]);

	mible_mesh_event_params_t evt = {
		.config_msg = {
			.opcode = {
				.company_id = MIBLE_MESH_COMPANY_ID_SIG,
				.opcode = MIBLE_MESH_MSG_CONFIG_RELAY_SET,
			},
			.meta_data = {
				.rssi = ctx->recv_rssi,
				.src_addr = ctx->addr,
				.dst_addr = ctx->recv_dst,
				.appkey_index = ctx->app_idx,
				.netkey_index = ctx->net_idx,
				.elem_index = model->elem_idx,
			},
			{
				.relay_set = {
					.relay = buf->data[0],
					.relay_retrans_cnt = BT_MESH_TRANSMIT_COUNT(buf->data[1]),
					.relay_retrans_intvlsteps = ((buf->data[1])  >> 3),
				}
			},
		},
	};

	event_post(MIBLE_MESH_EVENT_CONFIG_MESSAGE_CB, &evt);

	bt_mesh_model_msg_init(&msg, OP_RELAY_STATUS);
	net_buf_simple_add_u8(&msg, bt_mesh_relay_get());
	net_buf_simple_add_u8(&msg, bt_mesh_relay_retransmit_get());

	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
		LOG_ERR("Unable to send Relay Status response");
	}

	return 0;
}

int mible_mesh_node_reset(void)
{
	bt_mesh_reset();

	return 0;
}

static void node_reset_event_post(void)
{
	mible_mesh_event_params_t evt = {
		.config_msg = {
			.opcode = {
				.company_id = MIBLE_MESH_COMPANY_ID_SIG,
				.opcode = MIBLE_MESH_MSG_CONFIG_NODE_RESET,
			},
		},
	};

	evt.config_msg.meta_data.dst_addr = bt_mesh_primary_addr();
	evt.config_msg.meta_data.appkey_index = BT_MESH_KEY_DEV_LOCAL;

	event_post(MIBLE_MESH_EVENT_CONFIG_MESSAGE_CB, &evt);
}

static void reset_send_start(uint16_t duration, int err, void *cb_data)
{
	if (!err) {
		return;
	}

	LOG_ERR("Sending Node Reset Status failed (err %d)", err);

	node_reset_event_post();
}

static void reset_send_end(int err, void *cb_data)
{
	node_reset_event_post();
}

static int node_reset(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
		      struct net_buf_simple *buf)
{
	static const struct bt_mesh_send_cb reset_cb = {
		.start = reset_send_start,
		.end = reset_send_end,
	};

	BT_MESH_MODEL_BUF_DEFINE(msg, OP_NODE_RESET_STATUS, 0);

	LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u",
		ctx->net_idx, ctx->app_idx, ctx->addr, buf->len);

	bt_mesh_model_msg_init(&msg, OP_NODE_RESET_STATUS);

	if (bt_mesh_model_send(model, ctx, &msg, &reset_cb, NULL)) {
		LOG_ERR("Unable to send Node Reset Status");

		node_reset_event_post();
	}

	return 0;
}

struct mod_sub_list_ctx {
	uint16_t elem_idx;
	struct net_buf_simple *msg;
};

static enum bt_mesh_walk mod_sub_list_visitor(struct bt_mesh_model *mod, void *ctx)
{
	struct mod_sub_list_ctx *visit = ctx;
	int count = 0;
	int i;

	if (mod->elem_idx != visit->elem_idx) {
		return BT_MESH_WALK_CONTINUE;
	}

	for (i = 0; i < ARRAY_SIZE(mod->groups); i++) {
		if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
			continue;
		}

		if (net_buf_simple_tailroom(visit->msg) <
		    2 + BT_MESH_MIC_SHORT) {
			LOG_WRN("No room for all groups");
			return BT_MESH_WALK_STOP;
		}

		net_buf_simple_add_le16(visit->msg, mod->groups[i]);
		count++;
	}

	LOG_DBG("sublist: model %u:%x: %u groups", mod->elem_idx, mod->id,
	        count);

	return BT_MESH_WALK_CONTINUE;
}

static size_t mod_sub_list_clear(struct bt_mesh_model *mod)
{
	uint8_t *label_uuid;
	size_t clear_count;
	int i;

	/* Unref stored labels related to this model */
	for (i = 0, clear_count = 0; i < ARRAY_SIZE(mod->groups); i++) {
		if (!BT_MESH_ADDR_IS_VIRTUAL(mod->groups[i])) {
			if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
				mod->groups[i] = BT_MESH_ADDR_UNASSIGNED;
				clear_count++;
			}

			continue;
		}

		label_uuid = bt_mesh_va_label_get(mod->groups[i]);

		mod->groups[i] = BT_MESH_ADDR_UNASSIGNED;
		clear_count++;

		if (label_uuid) {
			bt_mesh_va_del(label_uuid, NULL);
		} else {
			LOG_ERR("Label UUID not found");
		}
	}

	return clear_count;
}

static enum bt_mesh_walk mod_sub_clear_visitor(struct bt_mesh_model *mod, void *user_data)
{
	mod_sub_list_clear(mod);

	return BT_MESH_WALK_CONTINUE;
}

static int send_mod_sub_status(struct bt_mesh_model *model,
			       struct bt_mesh_msg_ctx *ctx, uint8_t status,
			       uint16_t elem_addr, uint16_t sub_addr, uint8_t *mod_id,
			       bool vnd)
{
	BT_MESH_MODEL_BUF_DEFINE(msg, OP_MOD_SUB_STATUS, 9);

	LOG_DBG("status 0x%02x elem_addr 0x%04x sub_addr 0x%04x", status,
	        elem_addr, sub_addr);

	bt_mesh_model_msg_init(&msg, OP_MOD_SUB_STATUS);

	net_buf_simple_add_u8(&msg, status);
	net_buf_simple_add_le16(&msg, elem_addr);
	net_buf_simple_add_le16(&msg, sub_addr);

	if (vnd) {
		memcpy(net_buf_simple_add(&msg, 4), mod_id, 4);
	} else {
		memcpy(net_buf_simple_add(&msg, 2), mod_id, 2);
	}

	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
		LOG_ERR("Unable to send Model Subscription Status");
	}

	return 0;
}

static struct bt_mesh_model *get_model(struct bt_mesh_elem *elem,
				       struct net_buf_simple *buf, bool *vnd)
{
	if (buf->len < 4) {
		uint16_t id;

		id = net_buf_simple_pull_le16(buf);

		LOG_DBG("ID 0x%04x addr 0x%04x", id, elem->addr);

		*vnd = false;

		return bt_mesh_model_find(elem, id);
	} else {
		uint16_t company, id;

		company = net_buf_simple_pull_le16(buf);
		id = net_buf_simple_pull_le16(buf);

		LOG_DBG("Company 0x%04x ID 0x%04x addr 0x%04x", company, id,
		        elem->addr);

		*vnd = true;

		return bt_mesh_model_find_vnd(elem, company, id);
	}
}

static void cfg_msg_event_post(uint16_t opcode, struct bt_mesh_model *model,
			       struct bt_mesh_msg_ctx *ctx, uint16_t sub_addr, bool vnd)
{
	mible_mesh_event_params_t evt = {
		.config_msg = {
			.opcode = {
				.company_id = MIBLE_MESH_COMPANY_ID_SIG,
				.opcode = opcode,
			},
			.meta_data = {
				.rssi = ctx->recv_rssi,
				.src_addr = ctx->addr,
				.dst_addr = ctx->recv_dst,
				.appkey_index = ctx->app_idx,
				.netkey_index = ctx->net_idx,
				.elem_index = model->elem_idx,
			},
			{
				.model_sub_set = {
					.elem_addr = bt_mesh_primary_addr() + model->elem_idx,
					.address = sub_addr,
					.model_id = {
						.model_id = vnd ? model->vnd.id : model->id,
						.company_id = vnd ? MIBLE_MESH_COMPANY_ID_XIAOMI :
							      MIBLE_MESH_COMPANY_ID_SIG,
					}
				},
			},
		},
	};

	event_post(MIBLE_MESH_EVENT_CONFIG_MESSAGE_CB, &evt);
}

static int mod_sub_add(struct bt_mesh_model *model,
		       struct bt_mesh_msg_ctx *ctx,
		       struct net_buf_simple *buf)
{
	uint16_t elem_addr, sub_addr;
	struct bt_mesh_model *mod;
	struct bt_mesh_elem *elem;
	uint8_t *mod_id;
	uint8_t status;
	uint16_t *entry;
	bool vnd;

	if ((buf->len != 6U) && (buf->len != 8U)) {
		LOG_ERR("The message size for the application opcode is incorrect.");
		return -EMSGSIZE;
	}

	elem_addr = net_buf_simple_pull_le16(buf);
	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
		LOG_WRN("Prohibited element address");
		return -EINVAL;
	}

	sub_addr = net_buf_simple_pull_le16(buf);

	LOG_DBG("elem_addr 0x%04x, sub_addr 0x%04x", elem_addr, sub_addr);

	mod_id = buf->data;

	elem = bt_mesh_elem_find(elem_addr);
	if (!elem) {
		mod = NULL;
		vnd = (buf->len == 4U);
		status = STATUS_INVALID_ADDRESS;
		goto send_status;
	}

	mod = get_model(elem, buf, &vnd);
	if (!mod) {
		status = STATUS_INVALID_MODEL;
		goto send_status;
	}

	if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) {
		status = STATUS_INVALID_ADDRESS;
		goto send_status;
	}

	if (bt_mesh_model_find_group(&mod, sub_addr)) {
		/* Tried to add existing subscription */
		LOG_DBG("found existing subscription");
		status = STATUS_SUCCESS;
		goto send_status;
	}

	entry = bt_mesh_model_find_group(&mod, BT_MESH_ADDR_UNASSIGNED);
	if (!entry) {
		status = STATUS_INSUFF_RESOURCES;
		goto send_status;
	}

	*entry = sub_addr;
	status = STATUS_SUCCESS;

	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
		bt_mesh_model_sub_store(mod);
	}

	cfg_msg_event_post(MIBLE_MESH_MSG_CONFIG_MODEL_SUBSCRIPTION_ADD,
			   model, ctx, sub_addr, vnd);

send_status:
	return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
				   mod_id, vnd);
}

static int mod_sub_del(struct bt_mesh_model *model,
		       struct bt_mesh_msg_ctx *ctx,
		       struct net_buf_simple *buf)
{
	uint16_t elem_addr, sub_addr;
	struct bt_mesh_model *mod;
	struct bt_mesh_elem *elem;
	uint8_t *mod_id;
	uint16_t *match;
	uint8_t status;
	bool vnd;

	if ((buf->len != 6U) && (buf->len != 8U)) {
		LOG_ERR("The message size for the application opcode is incorrect.");
		return -EMSGSIZE;
	}

	elem_addr = net_buf_simple_pull_le16(buf);
	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
		LOG_WRN("Prohibited element address");
		return -EINVAL;
	}

	sub_addr = net_buf_simple_pull_le16(buf);

	LOG_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr);

	mod_id = buf->data;

	elem = bt_mesh_elem_find(elem_addr);
	if (!elem) {
		mod = NULL;
		vnd = (buf->len == 4U);
		status = STATUS_INVALID_ADDRESS;
		goto send_status;
	}

	mod = get_model(elem, buf, &vnd);
	if (!mod) {
		status = STATUS_INVALID_MODEL;
		goto send_status;
	}

	if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) {
		status = STATUS_INVALID_ADDRESS;
		goto send_status;
	}

	/* An attempt to remove a non-existing address shall be treated
	 * as a success.
	 */
	status = STATUS_SUCCESS;

	match = bt_mesh_model_find_group(&mod, sub_addr);
	if (match) {
		*match = BT_MESH_ADDR_UNASSIGNED;

		if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
			bt_mesh_model_sub_store(mod);
		}
	}

	cfg_msg_event_post(MIBLE_MESH_MSG_CONFIG_MODEL_SUBSCRIPTION_DELETE,
			   model, ctx, sub_addr, vnd);

send_status:
	return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
				   mod_id, vnd);
}

static int mod_sub_overwrite(struct bt_mesh_model *model,
			     struct bt_mesh_msg_ctx *ctx,
			     struct net_buf_simple *buf)
{
	uint16_t elem_addr, sub_addr;
	struct bt_mesh_model *mod;
	struct bt_mesh_elem *elem;
	uint8_t *mod_id;
	uint8_t status;
	bool vnd;

	if ((buf->len != 6U) && (buf->len != 8U)) {
		LOG_ERR("The message size for the application opcode is incorrect.");
		return -EMSGSIZE;
	}

	elem_addr = net_buf_simple_pull_le16(buf);
	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
		LOG_WRN("Prohibited element address");
		return -EINVAL;
	}

	sub_addr = net_buf_simple_pull_le16(buf);

	LOG_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr);

	mod_id = buf->data;

	elem = bt_mesh_elem_find(elem_addr);
	if (!elem) {
		mod = NULL;
		vnd = (buf->len == 4U);
		status = STATUS_INVALID_ADDRESS;
		goto send_status;
	}

	mod = get_model(elem, buf, &vnd);
	if (!mod) {
		status = STATUS_INVALID_MODEL;
		goto send_status;
	}

	if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) {
		status = STATUS_INVALID_ADDRESS;
		goto send_status;
	}

	if (ARRAY_SIZE(mod->groups) > 0) {
		bt_mesh_model_extensions_walk(mod,
					      mod_sub_clear_visitor, NULL);

		mod->groups[0] = sub_addr;
		status = STATUS_SUCCESS;

		if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
			bt_mesh_model_sub_store(mod);
		}
	} else {
		status = STATUS_INSUFF_RESOURCES;
	}

	cfg_msg_event_post(MIBLE_MESH_MSG_CONFIG_MODEL_SUBSCRIPTION_OVERWRITE,
			   model, ctx, sub_addr, vnd);

send_status:
	return send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
				   mod_id, vnd);
}

static int mod_sub_del_all(struct bt_mesh_model *model,
			   struct bt_mesh_msg_ctx *ctx,
			   struct net_buf_simple *buf)
{
	struct bt_mesh_model *mod;
	struct bt_mesh_elem *elem;
	uint16_t elem_addr;
	uint8_t *mod_id;
	uint8_t status;
	bool vnd;

	elem_addr = net_buf_simple_pull_le16(buf);
	if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
		LOG_WRN("Prohibited element address");
		return -EINVAL;
	}

	LOG_DBG("elem_addr 0x%04x", elem_addr);

	mod_id = buf->data;

	elem = bt_mesh_elem_find(elem_addr);
	if (!elem) {
		mod = NULL;
		vnd = (buf->len == 4U);
		status = STATUS_INVALID_ADDRESS;
		goto send_status;
	}

	mod = get_model(elem, buf, &vnd);
	if (!mod) {
		status = STATUS_INVALID_MODEL;
		goto send_status;
	}

	bt_mesh_model_extensions_walk(mod,
				      mod_sub_clear_visitor, NULL);

	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
		bt_mesh_model_sub_store(mod);
	}

	status = STATUS_SUCCESS;

	cfg_msg_event_post(MIBLE_MESH_MSG_CONFIG_MODEL_SUBSCRIPTION_DELETE_ALL,
			   model, ctx, BT_MESH_ADDR_UNASSIGNED, vnd);

send_status:
	return send_mod_sub_status(model, ctx, status, elem_addr,
				   BT_MESH_ADDR_UNASSIGNED, mod_id, vnd);
}

static int mod_sub_get(struct bt_mesh_model *model,
		       struct bt_mesh_msg_ctx *ctx,
		       struct net_buf_simple *buf)
{
	NET_BUF_SIMPLE_DEFINE(msg, BT_MESH_TX_SDU_MAX);
	struct mod_sub_list_ctx visit_ctx;
	struct bt_mesh_model *mod;
	struct bt_mesh_elem *elem;
	uint16_t addr, id;

	addr = net_buf_simple_pull_le16(buf);
	if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
		LOG_WRN("Prohibited element address");
		return -EINVAL;
	}

	id = net_buf_simple_pull_le16(buf);

	LOG_DBG("addr 0x%04x id 0x%04x", addr, id);

	bt_mesh_model_msg_init(&msg, OP_MOD_SUB_LIST);

	elem = bt_mesh_elem_find(addr);
	if (!elem) {
		net_buf_simple_add_u8(&msg, STATUS_INVALID_ADDRESS);
		net_buf_simple_add_le16(&msg, addr);
		net_buf_simple_add_le16(&msg, id);
		goto send_list;
	}

	mod = bt_mesh_model_find(elem, id);
	if (!mod) {
		net_buf_simple_add_u8(&msg, STATUS_INVALID_MODEL);
		net_buf_simple_add_le16(&msg, addr);
		net_buf_simple_add_le16(&msg, id);
		goto send_list;
	}

	net_buf_simple_add_u8(&msg, STATUS_SUCCESS);

	net_buf_simple_add_le16(&msg, addr);
	net_buf_simple_add_le16(&msg, id);

	visit_ctx.msg = &msg;
	visit_ctx.elem_idx = mod->elem_idx;
	bt_mesh_model_extensions_walk(mod,
				      mod_sub_list_visitor,
				      &visit_ctx);

send_list:
	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
		LOG_ERR("Unable to send Model Subscription List");
	}

	return 0;
}

static const struct bt_mesh_model_op cfg_srv_op[] = {
	{ OP_DEFAULT_TTL_GET,          BT_MESH_LEN_EXACT(0),   default_ttl_get },
	{ OP_DEFAULT_TTL_SET,          BT_MESH_LEN_EXACT(1),   default_ttl_set },
	{ OP_NET_TRANSMIT_GET,         BT_MESH_LEN_EXACT(0),   net_transmit_get },
	{ OP_NET_TRANSMIT_SET,         BT_MESH_LEN_EXACT(1),   net_transmit_set },
	{ OP_RELAY_GET,                BT_MESH_LEN_EXACT(0),   relay_get },
	{ OP_RELAY_SET,                BT_MESH_LEN_EXACT(2),   relay_set },
	{ OP_MOD_SUB_ADD,              BT_MESH_LEN_MIN(6),     mod_sub_add },
	{ OP_MOD_SUB_DEL,              BT_MESH_LEN_MIN(6),     mod_sub_del },
	{ OP_MOD_SUB_OVERWRITE,        BT_MESH_LEN_MIN(6),     mod_sub_overwrite },
	{ OP_MOD_SUB_DEL_ALL,          BT_MESH_LEN_MIN(4),     mod_sub_del_all },
	{ OP_MOD_SUB_GET,              BT_MESH_LEN_EXACT(4),   mod_sub_get },
	{ OP_NODE_RESET,               BT_MESH_LEN_EXACT(0),   node_reset },
	BT_MESH_MODEL_OP_END,
};

static int vendor_msg_recv(struct bt_mesh_model *model,
			    struct bt_mesh_msg_ctx *ctx,
			    struct net_buf_simple *buf)
{
	mible_mesh_event_params_t evt = {
		.generic_msg = {
			.opcode = {
				.company_id = MIBLE_MESH_COMPANY_ID_XIAOMI,
				.opcode = buf->data[-3],
			},
			.buf_len = buf->len,
			.meta_data = {
				.rssi = ctx->recv_rssi,
				.src_addr = ctx->addr,
				.dst_addr = ctx->recv_dst,
				.appkey_index = ctx->app_idx,
				.netkey_index = ctx->net_idx,
				.elem_index = model->elem_idx,
			},
		},
	};

	if (buf->len > 8) {
		evt.generic_msg.p_buf = buf->data;
	} else {
		(void)memcpy(evt.generic_msg.buff, buf->data, buf->len);
	}

	event_post(MIBLE_MESH_EVENT_GENERIC_MESSAGE_CB, &evt);

	return 0;
}

static const struct bt_mesh_model_op vendor_miot_op[] = {
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_GET, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_SET, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_SET_NOACK, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_SYNC_PROPS_RSP, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_ACTION, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_INDICATION_ACK, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_VENDOR_CONFIG_REQ, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SCENE_STORE, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SCENE_DELETE, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SCENE_RECALL, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SCENE_POS_ADD, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SCENE_POS_DEL, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_DEV_CONFIG_GET, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_DEV_CONFIG_SET, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   vendor_msg_recv },

	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_STATUS, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   msg_ignore },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_SYNC_PROPS_REQ, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   msg_ignore },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_EVENT, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   msg_ignore },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_EVENT_TLV, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   msg_ignore },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_ACTION_ACK, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   msg_ignore },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_INDICATION, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   msg_ignore },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SCENE_STATE, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   msg_ignore },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_DEV_TRACE, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   msg_ignore },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_DEV_CONFIG_STATUS, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   msg_ignore },
	{ BT_MESH_MODEL_OP_3(MIBLE_MESH_MIOT_SPEC_VENDOR_CONFIG_RSP, MIBLE_MESH_COMPANY_ID_XIAOMI),
	  0,   msg_ignore },

	BT_MESH_MODEL_OP_END,
};

static const struct bt_mesh_model_op vendor_mjia_op[] = {
	BT_MESH_MODEL_OP_END,
};

static int cfg_srv_init(struct bt_mesh_model *model)
{
	/*
	 * Configuration Model security is device-key based and only the local
	 * device-key is allowed to access this model.
	 */
	model->keys[0] = BT_MESH_KEY_DEV_LOCAL;

	return 0;
}

static const struct bt_mesh_model_cb cfg_srv_cb = {
    .init = cfg_srv_init,
};

static struct bt_mesh_model root_models[] = {
	BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_CFG_SRV,
		         cfg_srv_op, NULL, NULL, &cfg_srv_cb),
#if defined(MI_MESH_TEMPLATE_LIGHTNESS)		|| \
    defined(MI_MESH_TEMPLATE_LIGHTCTL)		|| \
    defined(MI_MESH_TEMPLATE_ONE_KEY_SWITCH)	|| \
    defined(MI_MESH_TEMPLATE_TWO_KEY_SWITCH)	|| \
    defined(MI_MESH_TEMPLATE_THREE_KEY_SWITCH)	|| \
    defined(MI_MESH_TEMPLATE_FAN)
	BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV,
		      gen_onoff_srv_op, NULL, NULL),
#endif
#if defined(MI_MESH_TEMPLATE_LIGHTNESS)	|| \
    defined(MI_MESH_TEMPLATE_LIGHTCTL)
	BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV,
		      light_lightness_srv_op, NULL, NULL),
#endif
};

static struct bt_mesh_model vendor_models[] = {
	BT_MESH_MODEL_VND(MIBLE_MESH_COMPANY_ID_XIAOMI,
			  MIBLE_MESH_MIOT_SPEC_SERVER_MODEL,
			  vendor_miot_op, NULL, NULL ),
	BT_MESH_MODEL_VND(MIBLE_MESH_COMPANY_ID_XIAOMI,
			  MIBLE_MESH_MIJIA_SERVER_MODEL,
			  vendor_mjia_op, NULL, NULL )
};

static struct bt_mesh_model secondary_0_models[] = {
#if defined(MI_MESH_TEMPLATE_TWO_KEY_SWITCH) || \
    defined(MI_MESH_TEMPLATE_THREE_KEY_SWITCH)
	BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV,
		      gen_onoff_srv_op, NULL, NULL),
#endif
#if defined(MI_MESH_TEMPLATE_LIGHTCTL)
	BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV,
		      light_ctl_tmp_srv_op, NULL, NULL),
#endif
};

static struct bt_mesh_model secondary_1_models[] = {
	BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV,
		      gen_onoff_srv_op, NULL, NULL),
};

static struct bt_mesh_elem elements[] = {
	BT_MESH_ELEM(0, root_models, vendor_models),
#if defined(MI_MESH_TEMPLATE_TWO_KEY_SWITCH)	|| \
    defined(MI_MESH_TEMPLATE_LIGHTCTL)		|| \
    defined(MI_MESH_TEMPLATE_THREE_KEY_SWITCH)
	BT_MESH_ELEM(0, secondary_0_models, BT_MESH_MODEL_NONE),
#endif
#if defined(MI_MESH_TEMPLATE_THREE_KEY_SWITCH)
	BT_MESH_ELEM(0, secondary_1_models, BT_MESH_MODEL_NONE),
#endif
};

static const struct bt_mesh_comp comp = {
	.cid = BT_COMP_ID_LF,
	.elem = elements,
	.elem_count = ARRAY_SIZE(elements),
};

static const struct bt_mesh_prov prov = {
};

static uint8_t bt_mesh_relay_prio_req_cb(bool local_match,
					 const struct bt_mesh_msg_ctx *ctx, uint8_t *xmit)
{
	uint8_t prio = 0;

	if (local_match) {
		prio = 1;
	}

	return prio;
}

int mible_mesh_device_init_stack(void)
{
	int err;
	mible_mesh_event_params_t param = {};

	err = bt_mesh_init(&prov, &comp);
	if (err) {
		MI_LOG_ERROR("Initializing mesh failed (err %d)\n", err);
		return err;
	}

#if defined(CONFIG_BT_MESH_RELAY_PRIORITY)
	bt_mesh_relay_priority_cb_reg(bt_mesh_relay_prio_req_cb);
#endif /* CONFIG_BT_MESH_RELAY_PRIORITY */

	event_post(MIBLE_MESH_EVENT_STACK_INIT_DONE, &param);

	return MI_SUCCESS;
}

static struct bt_mesh_model * mod_find(struct bt_mesh_elem *elem, uint32_t opcode)
{
	for (int i = 0; i < elem->model_count; i++) {
		for (int j = 0; elem->models[i].op[j].func; j++) {
			if (elem->models[i].op[j].opcode == opcode) {
				return &elem->models[i];
			}
		}
	}

	for (int i = 0; i < elem->vnd_model_count; i++) {
		for (int j = 0; elem->vnd_models[i].op[j].func; j++) {
			if (elem->vnd_models[i].op[j].opcode == opcode) {
				return &elem->vnd_models[i];
			}
		}
	}

	return NULL;
}

static struct bt_mesh_model * mod_find_by_id(struct bt_mesh_elem *elem,
					     uint16_t company_id, uint16_t id)
{
	if (!company_id) {
		for (int i = 0; i < elem->model_count; i++) {
			if (elem->models[i].id == id) {
				return &elem->models[i];
			}
		}

		return NULL;
	}

	for (int i = 0; i < elem->vnd_model_count; i++) {
		if (elem->vnd_models[i].vnd.id == id &&
		    elem->vnd_models[i].vnd.company == company_id) {
			return &elem->vnd_models[i];
		}
	}

	return NULL;
}

int mible_mesh_device_set_provsion_data(mible_mesh_provisioning_data_t *param)
{
	int err;

	err = bt_mesh_provision(param->netkey, param->net_idx, param->flags,
				param->iv, param->address, param->devkey);
	if (err) {
		LOG_ERR("Provisoning failed (err %d)", err);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

int mible_mesh_device_unprovsion_done(void)
{
	return mible_reboot();
}

int mible_mesh_device_set_relay(uint8_t enabled, uint8_t count, uint8_t interval)
{
	int err;
	enum bt_mesh_feat_state state;

	if (enabled) {
		state = BT_MESH_FEATURE_ENABLED;
	} else {
		state = BT_MESH_FEATURE_DISABLED;
	}

	err = bt_mesh_relay_set(state,
				BT_MESH_TRANSMIT(count, BT_MESH_TRANSMIT_INT(interval << 3)));
	if (err && err != -EALREADY) {
		LOG_ERR("Unable set relay params state=0x%02x (err %d)",
			state, err);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

int mible_mesh_device_get_relay(uint8_t *enabled, uint8_t *count, uint8_t *step)
{
	enum bt_mesh_feat_state state;

	state = bt_mesh_relay_get();
	if (state != BT_MESH_FEATURE_ENABLED) {
		*enabled = 0;
		return MI_SUCCESS;
	}

	*enabled = 1;
	*step = BT_MESH_TRANSMIT_INT(bt_mesh_relay_retransmit_get());
	*count = BT_MESH_TRANSMIT_COUNT(bt_mesh_relay_retransmit_get());

	return MI_SUCCESS;
}

int mible_mesh_device_set_nettx(uint8_t ttl, uint8_t count, uint8_t interval)
{
	int err;

	err = bt_mesh_default_ttl_set(ttl);
	if (err) {
		LOG_ERR("Unable set ttl 0x%02x", ttl);
		return MI_ERR_INTERNAL;
	}

	bt_mesh_net_transmit_set(BT_MESH_TRANSMIT(count,
						  BT_MESH_TRANSMIT_INT(interval << 3)));
	return MI_SUCCESS;
}

int mible_mesh_device_get_nettx(uint8_t *ttl, uint8_t *count, uint8_t *step)
{
	*ttl = bt_mesh_default_ttl_get();
	*step = (bt_mesh_net_transmit_get() >> 3);
	*count = BT_MESH_TRANSMIT_COUNT(bt_mesh_net_transmit_get());
	return MI_SUCCESS;
}

int mible_mesh_device_set_seq(uint16_t element, uint32_t seq)
{
	bt_mesh.seq = MIN(seq, seq - 1);

	bt_mesh_next_seq();

    	return MI_SUCCESS;
}

int mible_mesh_device_get_seq(uint16_t element, uint32_t* seq,
			      uint32_t* iv, uint8_t* flags)
{
	*seq = bt_mesh.seq;
	*iv = bt_mesh.iv_index;
	*flags = atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ?
		 true : false;

	return MI_SUCCESS;
}

int mible_mesh_device_snb_start(bool enable)
{
	bt_mesh_beacon_set(enable);

	return MI_SUCCESS;
}

int mible_mesh_device_set_appkey(mible_mesh_op_t op, uint16_t netkey_index,
				 uint16_t appkey_index, uint8_t *appkey)
{
	if (op != MIBLE_MESH_OP_ADD) {
		LOG_ERR("Op 0x%02x not support", op);
		return MI_ERR_NOT_FOUND;
	}

	if (bt_mesh_app_key_add(appkey_index, netkey_index, appkey)) {
		LOG_ERR("Unable add app key net_idx 0x%04x app_idx 0x%04x",
			netkey_index, appkey_index);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

static int mod_bind(struct bt_mesh_model *model, uint16_t key_idx)
{
	int i;

	LOG_DBG("model %p key_idx 0x%03x", model, key_idx);

	if (!bt_mesh_app_key_exists(key_idx)) {
		LOG_ERR("Appkey idx 0x%04x not found", key_idx);
		return MI_ERR_NOT_FOUND;
	}

	for (i = 0; i < ARRAY_SIZE(model->keys); i++) {
		/* Treat existing binding as success */
		if (model->keys[i] == key_idx) {
			return MI_SUCCESS;
		}
	}

	for (i = 0; i < ARRAY_SIZE(model->keys); i++) {
		if (model->keys[i] == BT_MESH_KEY_UNUSED) {
			model->keys[i] = key_idx;

			if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
				bt_mesh_model_bind_store(model);
			}

			return MI_SUCCESS;
		}
	}

	LOG_ERR("Model bind failed");

	return MI_ERR_RESOURCES;
}

static int mod_unbind(struct bt_mesh_model *model, uint16_t key_idx, bool store)
{
	int i;

	LOG_DBG("model %p key_idx 0x%03x store %u", model, key_idx, store);

	if (!bt_mesh_app_key_exists(key_idx)) {
		LOG_ERR("Appkey idx 0x%04x not found", key_idx);
		return MI_ERR_NOT_FOUND;
	}

	for (i = 0; i < ARRAY_SIZE(model->keys); i++) {
		if (model->keys[i] != key_idx) {
			continue;
		}

		model->keys[i] = BT_MESH_KEY_UNUSED;

		if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
			bt_mesh_model_bind_store(model);
		}
	}

	return MI_SUCCESS;
}

int mible_mesh_device_set_model_app(mible_mesh_op_t op, uint16_t elem_index,
				    uint16_t company_id, uint16_t model_id,
				    uint16_t appkey_index)
{
	struct bt_mesh_elem *elem;
	struct bt_mesh_model *mod;

	if (elem_index > bt_mesh_elem_count()) {
		LOG_ERR("Too big element index 0x%02x", elem_index);
		return MI_ERR_RESOURCES;
	}

	elem = &elements[elem_index];

	if (company_id != MIBLE_MESH_COMPANY_ID_XIAOMI) {
		mod = mod_find_by_id(elem, 0x00, model_id);
	} else {
		mod = mod_find_by_id(elem, company_id, model_id);
	}

	if (!mod) {
		LOG_ERR("Model cid 0x%04x id 0x%04x not found",
			company_id, model_id);
		return MI_ERR_NOT_FOUND;
	}

	if (op == MIBLE_MESH_OP_ADD) {
		return mod_bind(mod, appkey_index);
	} else {
		return mod_unbind(mod, appkey_index, true);
	}
}

int mible_mesh_device_set_sub_address(mible_mesh_op_t op, uint16_t element,
				      uint16_t company_id, uint16_t model_id,
				      uint16_t sub_addr)
{
	struct bt_mesh_elem *elem;
	struct bt_mesh_model *mod;
	uint16_t *entry;

	if (element > bt_mesh_elem_count()) {
		LOG_ERR("Too big element index 0x%02x", element);
		return MI_ERR_RESOURCES;
	}

	elem = &elements[element];

	if (company_id != MIBLE_MESH_COMPANY_ID_XIAOMI) {
		mod = mod_find_by_id(elem, 0x00, model_id);
	} else {
		mod = mod_find_by_id(elem, company_id, model_id);
	}

	if (!mod) {
		LOG_ERR("Model cid 0x%04x id 0x%04x not found",
			company_id, model_id);
		return MI_ERR_NOT_FOUND;
	}

	if (op == MIBLE_MESH_OP_ADD) {
		if (bt_mesh_model_find_group(&mod, sub_addr)) {
			/* Tried to add existing subscription */
			LOG_DBG("found existing subscription");
			return MI_SUCCESS;
		}

		entry = bt_mesh_model_find_group(&mod,
						 BT_MESH_ADDR_UNASSIGNED);
		if (!entry) {
			LOG_ERR("Model cid 0x%04x id 0x%04x subscription 0x%04x failed",
				company_id, model_id, sub_addr);
			return MI_ERR_RESOURCES;
		}

		*entry = sub_addr;
	} else if (op == MIBLE_MESH_OP_DELETE) {
		entry = bt_mesh_model_find_group(&mod, sub_addr);
		if (entry) {
			*entry = BT_MESH_ADDR_UNASSIGNED;
		}
	} else if (op == MIBLE_MESH_OP_OVERWRITE) {
		bt_mesh_model_extensions_walk(mod,
					      mod_sub_clear_visitor, NULL);

		mod->groups[0] = sub_addr;
	} else if (op == MIBLE_MESH_OP_DELETE_ALL) {
		bt_mesh_model_extensions_walk(mod,
					      mod_sub_clear_visitor,
					      NULL);
	}

	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
		bt_mesh_model_sub_store(mod);
	}

	return MI_SUCCESS;
}

int mible_mesh_device_get_sub_address(uint16_t *addr, uint16_t max_count)
{
	int i, j;
	struct bt_mesh_model *mod = &vendor_models[0];

	for (i = 0, j = 0; i < ARRAY_SIZE(mod->groups); i++) {
		if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
			continue;
		}

		if (j >= max_count) {
			LOG_ERR("Insufficient space to store address");
			return MI_ERR_NO_MEM;
		}

		addr[j++] = mod->groups[i];
	}

	return MI_SUCCESS;
}

uint64_t mible_mesh_get_exact_systicks(void)
{
	return k_uptime_get();
}

int mible_mesh_node_generic_control(mible_mesh_access_message_t *param)
{
	NET_BUF_SIMPLE_DEFINE(msg, BT_MESH_TX_SDU_MAX);
	struct bt_mesh_elem *elem;
	struct bt_mesh_model *mod;
	struct bt_mesh_msg_ctx ctx = {
		.net_idx = param->meta_data.netkey_index,
		.app_idx = param->meta_data.appkey_index,
		.addr = param->meta_data.dst_addr,
		.send_ttl = BT_MESH_TTL_DEFAULT,
	};
	uint32_t op;

	elem = bt_mesh_elem_find(param->meta_data.src_addr);
	if (!elem) {
		LOG_ERR("Element 0x%04x not found", param->meta_data.src_addr);
		return MI_ERR_NOT_FOUND;
	}

	if(param->opcode.company_id == MIBLE_MESH_COMPANY_ID_XIAOMI) {
		op = BT_MESH_MODEL_OP_3(param->opcode.opcode,
				       	param->opcode.company_id);
    	} else {
        	op = param->opcode.opcode;
    	}

	mod = mod_find(elem, op);
	if (!mod) {
		LOG_ERR("Model not found (opcode 0x%08x)", op);
		return MI_ERR_NOT_FOUND;
	}

	bt_mesh_model_msg_init(&msg, op);

	if (param->buf_len <= 8) {
		net_buf_simple_add_mem(&msg, param->buff, param->buf_len);
	} else if (net_buf_simple_max_len(&msg) >= param->buf_len) {
        	net_buf_simple_add_mem(&msg, param->p_buf, param->buf_len);
    	} else {
		LOG_ERR("Too big data size");
        	return MI_ERR_DATA_SIZE;
	}

	if (bt_mesh_model_send(mod, &ctx, &msg, NULL, NULL)) {
		LOG_ERR("Unable to send mesh message");
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

/*  param in:reason
    0: power on; 
    1: watchdog
*/
uint8_t mible_mesh_get_reset_reason(void)
{
	uint8_t reset_reason = 0;

#if defined(CONFIG_BOARDCTL_RESET_CAUSE)
	int ret;
	struct boardioc_reset_cause_s cause;

	memset(&cause, 0, sizeof(cause));
	ret = boardctl(BOARDIOC_RESET_CAUSE, &cause);
	if (ret < 0){
		LOG_ERR("BOARDIOC_RESET_CAUSE get fail\n");
	}
	else{
		if(BOARDIOC_RESETCAUSE_SYS_RWDT == cause.cause){
			reset_reason = 1;
		}
	}
#endif

	return reset_reason;
}
